package cn.hy.dataSync.bean;

import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpResponse;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultConnectionKeepAliveStrategy;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.impl.nio.reactor.IOReactorConfig;
import org.apache.http.protocol.HttpContext;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;
import java.util.Map;
import java.util.concurrent.Future;


@Slf4j
@Component
public final class HttpAsyncClient {

    @Resource
    private HttpAsyncConfig httpAsyncConfig;

    private CloseableHttpAsyncClient asyncHttpClient;

    @PostConstruct
    private CloseableHttpAsyncClient initHttpAsyncClient() {
        asyncHttpClient = HttpAsyncClients.custom()
                .setMaxConnTotal(httpAsyncConfig.getMaxConnTotal())
                .setMaxConnPerRoute(httpAsyncConfig.getMaxConnPerRoute())
                .setKeepAliveStrategy(new DefaultConnectionKeepAliveStrategy() {
                    @Override
                    public long getKeepAliveDuration(HttpResponse response, HttpContext context) {
                        long keepAlive = super.getKeepAliveDuration(response, context);
                        if (keepAlive == -1) {
                            // 如果keep-alive值没有由服务器明确设置，那么保持连接持续n秒。
                            keepAlive = 10 * 1000;
                        }
                        return keepAlive;
                    }
                })
                .setDefaultRequestConfig(
                        RequestConfig.custom()
                                .setConnectionRequestTimeout(httpAsyncConfig.getConnectionRequestTimeout())
                                .setSocketTimeout(httpAsyncConfig.getSocketTimeout())
                                .setConnectTimeout(httpAsyncConfig.getConnectTimeout())
                                .build()
                )
                .setDefaultIOReactorConfig(
                        IOReactorConfig.custom()
                                .setSoReuseAddress(true)
                                .build()
                )
                .build();

        log.info("init asyncHttpClient success");
        asyncHttpClient.start();
        return asyncHttpClient;
    }

    @PreDestroy
    private void closeHttpAsyncClient() throws Exception {
        if (asyncHttpClient != null) {
            asyncHttpClient.close();
            log.info("close asyncHttpClient success");
        }
    }

    public Future<HttpResponse> doRequest(final String url, final Map<String, String> headers,
                                          final String content, final String encoding,
                                          final FutureCallback<HttpResponse> callback) {
        if (asyncHttpClient == null) {
            throw new RuntimeException("asyncHttpClient incomplete initialization");
        }

        if (!asyncHttpClient.isRunning()) {
            asyncHttpClient.start();
        }

        final HttpPost httpPost = new HttpPost(url);

        if (!headers.isEmpty()) {
            headers.forEach(httpPost::addHeader);
        }

        httpPost.setEntity(new StringEntity(content, encoding));

        return asyncHttpClient.execute(httpPost, callback);
    }
}
